/*
 * Copyright (c) 2009, Jan Pastor. All Rights Reserved.
 */

package sk.jp.jitag.dao.db4o;

import java.util.Collection;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.db4o.ObjectSet;
import com.db4o.ext.ExtObjectContainer;

import sk.jp.commons.exception.ApplicationException;
import sk.jp.commons.utils.ExceptionUtils;
import sk.jp.commons.utils.LogMsg;

/**
 * Miscellanous utilities for working with <i>Db4o</i>.
 */
public class Db4oUtils {

    private static final Logger log = LoggerFactory.getLogger(Db4oUtils.class);

    private Db4oUtils() {
    }

    //----------------------------------------------------------------------

    /**
     * Dumps all persisted instances of specified entity to log.
     * @param db
     * @param entityClass
     * @param <T>
     */
    public static <T extends BaseEntity> void listEntities(ExtObjectContainer db,
                                                           Class<T> entityClass){
        final StringBuilder buf = new StringBuilder();
        buf.append("\nTABLE: ").append(entityClass.getSimpleName());
        final ObjectSet<T> entities = db.query(entityClass);
        buf.append(" (count: ").append(entities.size()).append(')');
        for (T entity : entities) {
            buf.append("\n  [").append(db.getID(entity)).append("] ").append(entity.toString());
        }
        log.trace("Table dump: "+ buf);
    }

    /**
     * Dumps collected persisted instances of specified entity to log.
     * @param db
     * @param entityClass
     * @param <T>
     */
    public static <T extends BaseEntity> void listEntities(Collection<T> entities,
                                                           Class<T> entityClass){
        final StringBuilder buf = new StringBuilder();
        buf.append("\nTABLE (subset): ").append(entityClass.getSimpleName());
        buf.append(" (count: ").append(entities.size()).append(')');
        for (T entity : entities) {
            buf.append("\n  [#").append(entity.hashCode()).append("] ")
                .append(entity.toString());
        }
        log.trace("Table subset dump: "+ buf);
    }

    /**
     * Returns first item from given set.
     * <p>This method is designed for process query results - retrieve a specified item.
     * @param set of entities
     * @param <T> entity class
     * @return entity instance or null if given set is null or empty.
     */
    public static <T> T first(List<T> set){
        if(log.isTraceEnabled()){
            log.trace(LogMsg.inp("set", set));
        }

        return set == null || set.isEmpty() ? null : set.get(0);
    }

    /**
     * Returns first item from set or null if set is empty.
     * <p>If set contains more than one item or if set is null, throws exception.
     * <p>This method is designed for process query results - retrieve a specified item.
     * @param set
     * @param <T> entity class
     * @return first item from set or null if set is empty.
     * @throws NullPointerException if set is null.
     * @throws IllegalArgumentException if set has more than one items.
     */
    public static <T> T getOne(List<T> set){
        if(log.isTraceEnabled()){
            log.trace(LogMsg.inp("set", set));
        }

        if(set == null){
            throw new NullPointerException("Set is null");
        }
        if(set.size() > 1){
            throw new ApplicationException("Invalid result: Only one instance was expected",
                                           "sk.jp.jitag.dao.db4o.Db4oUtils.err.001",
                                           ExceptionUtils.paramsToMap("setSize", set.size()));
        }
        return first(set);
    }

    /**
     * Returns first item from set.
     * <p>If set contains more than one item or if set is empty or null, throws exception.
     * <p>This method is designed for process query results - retrieve a specified item.
     * @param set
     * @param <T> entity class
     * @return first item from set.
     * @throws NullPointerException if set is null.
     * @throws IllegalArgumentException if set has zero or more than one items.
     */
    public static <T> T getOneRequired(List<T> set){
        if(log.isTraceEnabled()){
            log.trace(LogMsg.inp("set", set));
        }

        T one = getOne(set);
        if(one == null){
            throw new ApplicationException("No instance was found",
                                           "sk.jp.jitag.dao.db4o.Db4oUtils.err.002");
        }
        return one;
    }

}
